<?php
// +----------------------------------------------------------------------
// | LwwanPHP[基于ThinkPHP5.1开发]
// +----------------------------------------------------------------------
// | Copyright (c) 2016-2019 http://www.lwwan.com
// +----------------------------------------------------------------------
// | Author 似水星辰 [ 2630481389@qq.com ]
// +----------------------------------------------------------------------
// | 星辰工作室 QQ群331378225
// +----------------------------------------------------------------------
namespace app\order\model;

use think\Db;
use think\Model as ThinkModel;

/**
 * 订单商品列表
 * @package app\order\model
 */
class OrderGoods extends ThinkModel
{

    // 设置当前模型对应的完整数据表名称
    protected $table = '__ORDER_GOODS_LIST__';

    // 自动写入时间戳
    protected $autoWriteTimestamp = false;


    public static function get_one_goods($order_sn = null)
    {
        return self::where('order_sn', $order_sn)->find();
    }

    /**
     * 取消商城订单第一步-锁定
     * 为避免支付超时 ，家或者平台自动取消订单造成支付延误到账的问题，
     * 取消时先不退还资产，将订单状态改成-2 支付回调仍然可以执行，半小时后，再执行资金退还和库存回退
     * @author php迷途小书童
     * @editer 晓风 2021-08-07
     * @created 2020/12/23 13:55
     */
    public static function cancelOrder(\app\common\model\Order $order, $type = 0, $return_id = 0)
    {
        Db::startTrans();
        try {
            if ($order["status"] != 0) {
                throw new \Exception('订单状态不正确');
            }
            if (!in_array($order["order_type"], [3])) {
                throw new \Exception('订单类型不相符');
            }
            //订单状态
            \app\common\model\Order::where('order_sn', $order['order_sn'])->update([
                "cancel_time" => time(),//存入取消时间，按照这个时间半小时判断status 是-2 就退库存
                'status' => -2,
                "return_id" => $return_id
            ]);
            //商品明细
            OrderGoods::where('order_sn', $order['order_sn'])->setField('order_status', -2);

            if ($type == 1) {
                $msg = "买家申请取消";
                \app\order\model\OrderAction::actionLog($order['order_sn'], $msg, -2, $order["user_id"]);
            } else {
                $msg = "系统超时自动取消中";
                \app\order\model\OrderAction::actionLog($order['order_sn'], $msg, -2);
            }
            $thumb = OrderGoods::where("order_sn", $order["order_sn"])->value("goods_thumb");
            \app\operation\model\SystemMessage::create([
                "to_user_id" => $order['user_id'],
                "title" => '您的订单已取消',
                "order_sn" => $order['order_sn'],
                "content" => "您的商品订单：" . $order["order_sn"] . ",已由" . $msg . "\n系统约于45分钟内处理取消结算\n如涉及虚拟资产作废,涉及优惠券将原路退还",
                "msg_type" => 2,
                "thumb" => $thumb ?: 0,
                "custom" => json_encode(['type' => 'order', 'id' => $order['order_sn']])
            ]);
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            throw $e;
        }
        return true;
    }

    /**
     * 取消商城订单第二步-退还资产
     * @author 晓风<215628355@qq.com>
     * @created 2021-08-14
     */
    public static function cancelOrderRefund(\app\common\model\Order $order, $type = 0)
    {
        Db::startTrans();
        try {
            if ($order["status"] != -2) {
                throw new \Exception('订单状态不正确');
            }
            if (!in_array($order["order_type"], [3])) {
                throw new \Exception('订单类型不相符');
            }
            $time = time() - 1800;
            if ($order["cancel_time"] > $time) {
                throw new \Exception('时机未到');
            }
            self::cancelOrderSure($order, $order["cancel_time"], $order["return_id"]);
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            throw $e;
        }
        return true;
    }

    /**
     * 取消商城订单-用户端直接取消订单
     * @author 晓风<215628355@qq.com>
     * @created 2021-08-14
     */
    public static function cancelOrderUser(\app\common\model\Order $order, $return_id = 0)
    {
        Db::startTrans();
        try {
            if ($order["status"] != 0) {
                throw new \Exception('订单状态不正确');
            }
            if (!in_array($order["order_type"], [3])) {
                throw new \Exception('订单类型不相符');
            }
            self::cancelOrderSure($order, time(), $return_id);

            $thumb = OrderGoods::where("order_sn", $order["order_sn"])->value("goods_thumb");
            \app\operation\model\SystemMessage::create([
                "to_user_id" => $order['user_id'],
                "title" => '您的订单已取消',
                "order_sn" => $order['order_sn'],
                "content" => "您的商品订单：" . $order["order_sn"] . ",已取消支付 \n如涉及虚拟资产作废,涉及优惠券将原路退还",
                "msg_type" => 2,
                "thumb" => $thumb ?: 0,
                "custom" => json_encode(['type' => 'order', 'id' => $order['order_sn']])
            ]);
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            throw $e;
        }
        return true;
    }

    /**
     * 取消订单-执行
     * 请勿直接调用此方法
     * @param \app\common\model\Order $order
     * @param int $cancel_time
     * @param int $return_id
     * @author 晓风<215628355@qq.com>
     */
    protected static function cancelOrderSure(\app\common\model\Order $order, int $cancel_time, int $return_id)
    {
        //订单状态
        \app\common\model\Order::where('order_sn', $order['order_sn'])->setField([
            "cancel_time" => $cancel_time,
            'status' => -1,
            "return_id" => $return_id
        ]);
        //商品明细
        OrderGoods::where('order_sn', $order['order_sn'])->setField('order_status', -1);
        //退还优惠券
        \app\operation\model\CouponRecord::where('order_sn', $order['order_sn'])->update(['status' => 1, 'use_time' => 0, 'order_sn' => '']);
        $order_goods = OrderGoods::where('order_sn', $order['order_sn'])->select();
        //商品库存回退
        foreach ($order_goods as $v) {

            if ($v['sku_id']) {
                \app\goods\model\GoodsSku::where('sku_id', $v['sku_id'])->update([
                    "stock" => ["inc", $v['num']],
                    "sales_sum" => ["dec", $v['num']],
                ]);
                $before = \app\goods\model\GoodsSku::where('sku_id', $v['sku_id'])->value("stock,sku_sn as sn");
            } else {
                $before = \app\goods\model\Goods::where('id', $v['goods_id'])->field("stock,sn")->find();
            }
            \app\goods\model\Goods::where('id', $v['goods_id'])->update([
                "stock" => ["inc", $v['num']],
                "sales_sum" => ["dec", $v['num']],
            ]);
            //存入库存变化
            \app\goods\model\GoodsStockLog::AddStockLog(
                $v['goods_id'],
                $v['sku_id'],
                $order['order_sn'],
                $before['stock'] ?? 0,
                $v['num'],
                bcadd($before['stock'] ?? 0, $v['num']),
                6,
                defined('UID') ? UID : 0,
                lang('取消订单结算') . ($before ? '' : '->' . lang('商品已下架')),
                $before['sn']
            );
        }
        $msg = "订单已取消";
        \app\order\model\OrderAction::addLog($order['order_sn'], $msg, -1);
        //分佣钩子
        //\think\facade\Hook::exec([\app\order\event\Order::class,"cancel"],['orders'=>$order_data]); 
    }


    /**
     * 确认收货
     * @author php迷途小书童
     * @created 2020/12/23 11:48
     * @editer 晓风 2021-08-07
     */
    public static function receiveOrder(\app\common\model\Order $order, $type = 0)
    {
        $time = time();
        Db::startTrans();
        try {
            if ($order["status"] != 2) {
                throw new \Exception('订单状态不正确');
            }
            if (!in_array($order["order_type"], [3])) {
                throw new \Exception('订单类型不相符');
            }

            $refund = OrderRefund::where("status", "in", [0, 1, 2])->where("order_sn", $order["order_sn"])->count();
            if ($refund > 0) {
                throw new \Exception('该订单有' . $refund . "笔进行中的退款，请先处理退款");
            }

            \app\common\model\Order::where('order_sn', $order['order_sn'])->where("status", 2)->update(['status' => 3]);
            //变更订单商品表
            OrderGoods::where('order_sn', $order['order_sn'])->where("order_status", 2)->update(['order_status' => 3]);
            //Db::name('order_goods_info')->where('order_sn', $order['order_sn'])->update(['sign_time' => $time]);  
            OrderGoodsExpress::where('order_sn', $order['order_sn'])->update(['receive_time' => $time]);

            if ($type == 1) {
                $msg = "买家确认收货";
                \app\order\model\OrderAction::addLog($order['order_sn'], $msg, 3, $order["user_id"]);
            } else if ($type == 2) {
                $msg = "平台确认收货";
                \app\order\model\OrderAction::addLog($order['order_sn'], $msg, 3);
            } else {
                $msg = "系统超时自动确认收货";
                \app\order\model\OrderAction::addLog($order['order_sn'], $msg, 3);
            }
            $thumb = OrderGoods::where("order_sn", $order["order_sn"])->value("goods_thumb");
            \app\operation\model\SystemMessage::create([
                "to_user_id" => $order['user_id'],
                "title" => '您的订单已收货',
                "order_sn" => $order['order_sn'],
                "content" => "您的商品订单：" . $order["order_sn"] . ",已由" . $msg . ",\n如涉及虚拟资产，请查看是否到账,注意退单的部分不会入账",
                "msg_type" => 2,
                "thumb" => $thumb ?: 0,
                "custom" => json_encode(['type' => 'order', 'id' => $order['order_sn']])
            ]);
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            throw $e;
        }
        return true;
    }

    /**
     * 创建商城订单
     * @param array $data 下单参数数组
     * @param array $user 会员数据数组
     * @return array
     * @throws \Exception
     * @author 晓风<215628355@qq.com> 重构
     */
    public static function addGoodsOrder($data, $user)
    {
        $order_info = json_decode($data['order_info'], true);
        //获取订单的配送类型 
        $send_type = $order_info['send_type'] ?? 0;
        //优惠券id
        $coupon_id = $data['coupon_id'] ?: 0;

        $share_sign = $data['share_sign'] ?? '';
        //生成订单号
        $order_no = get_order_sn('GD');
        //保存订单商品表信息
        $payable_money = $order_money = $cost_price_total = 0;
        $goods_list = $where = $where1 = [];
        foreach ($order_info['goods'] as $g) {
            //删除购物车信息
            Db::name('goods_cart')
                ->where(['user_id' => $user['id'], 'goods_id' => $g['id'], 'sku_id' => $g['sku_id']])
                ->delete();
            $num = $g['number'];//下单数量
            $good_info = \app\goods\model\Goods::get($g['id']);
            if ($good_info["stock"] < $num) {
                throw new \Exception($good_info['name'] . "被抢光了，请重新下单");
            }
            $sku_id = $g['sku_id'] ?? 0;
            $shop_price = $good_info["shop_price"];
            $cost_price = $good_info["cost_price"];
            $good_sn = $good_info["sn"];
            $stock = $good_info["stock"];
            $sku_name = "";
            if ($sku_id) {
                $skuInfo = \app\goods\model\GoodsSku::where(['sku_id' => $sku_id, 'goods_id' => $g['id']])->find();
                if ($skuInfo["stock"] < $num) {
                    throw new \Exception($good_info['name'] . "被抢光了，请重新下单");
                }
                $sku_name = $skuInfo['key_name'];
                $shop_price = $skuInfo["shop_price"];
                $cost_price = $skuInfo["cost_price"];
                $good_sn = $skuInfo["sku_sn"];
                $stock = $skuInfo["stock"];
            }
            $goods_money = bcmul($shop_price, $g['number'], 2);
            $cost_price_total = bcadd($cost_price_total, bcmul($cost_price, $g['number'], 2), 2);
            $payable_money = bcadd($payable_money, $goods_money, 2);
            $order_money = bcadd($order_money, $goods_money, 2);

            $profit = $cost_price > 0 ? bcmul($shop_price - $cost_price, $num, 2) : 0;

            $goods_list[] = [
                'order_sn' => $order_no,
                'goods_id' => $g['id'],
                'goods_name' => $good_info['name'],
                'shop_price' => $shop_price,
                'cost_price' => $cost_price,//成本价
                'sku_id' => $sku_id,
                'num' => $num,//下单数量
                'goods_thumb' => $good_info['thumb'],
                'order_status' => 0,
                'sku_name' => $sku_name,
                'goods_money' => $goods_money,
                'share_sign' => $share_sign,
                'create_time' => time(),
                'sender_id' => $good_info['sender_id'],
                'profit' => $profit,//订单中该商品利润
                'goods_sn' => $good_sn,
            ];
            //减库存
            if ($sku_id) {
                \app\goods\model\GoodsSku::where("sku_id", $sku_id)->update([
                    "stock" => ["dec", $num],
                    "sales_sum" => ["inc", $num],
                    "sales_num_new" => ["inc", $num]
                ]);
            }
            \app\goods\model\Goods::where("id", $good_info['id'])->update([
                "stock" => ["dec", $num],
                "sales_sum" => ["inc", $num]
            ]);
            //存入库存变化
            \app\goods\model\GoodsStockLog::AddStockLog(
                $good_info['id'],
                $sku_id,
                $order_no,
                $stock,
                $num,
                $stock - $num,
                5,
                0,
                lang('用户购买'),
                $good_sn
            );
        }
        //插入订单商品表
        $goods_list_insert = (new self())->insertAll($goods_list);
        if ($goods_list_insert != count($goods_list)) {
            throw new \Exception("下单失败，请重试");
        }
        //如果提交了优惠券id，则查询数据库中的优惠券
        $coupon_money = 0;
        if ($coupon_id) {
            //TODO 可使用优惠券待优化
            $coupon_record = new \app\operation\model\CouponRecord();
            $coupon = $coupon_record->get_user_coupon(['cr.user_id' => $user['id'], 'cr.id' => $coupon_id, 'cr.status' => 1]);
            if (!$coupon) {
                throw new \Exception(lang('优惠券无效，请重新下单'));
            }
            if (bccomp($payable_money, $coupon['money'], 2) < 0) {
                throw new \Exception(lang('优惠券金额不能大于商品总价'));
            }
            $payable_money = bcsub($payable_money, $coupon['money'], 2);
            $coupon_money = $coupon['money'];
            $coupon_update = $coupon_record->where(['id' => $data['coupon_id'], 'status' => 1])->update(['status' => 3, 'use_time' => time(), 'order_sn' => $order_no]);
            if (!$coupon_update) {
                throw new \Exception(lang('优惠券无效，请重新下单'));
            }
        }
        //如果有运费，加上
        if ($order_info['express_price']) {
            //实际支付金额
            $payable_money = bcadd($payable_money, $order_info['express_price'], 2);
            //订单金额
            $order_money = bcadd($order_money, $order_info['express_price'], 2);
        }

        $pay_type = $data['pay_type'] ?? '';
        //若付款金额为0 则直接成功，若有分销则不走分销流程，请注意
        //如有需要，请将此处还设为0，可编写0元支付接口，类似余额支付，走回调流程即可
        $status = $pay_status = 0;
        if (bccomp($payable_money, 0, 2) == 0) {
            $pay_status = 1;
            $status = 1;
            $pay_type = "zero";
        }
        //组装订单信息
        $orderData = [
            'send_type' => $send_type,
            'user_id' => $user['id'],
            'order_sn' => $order_no,
            'order_money' => $order_money,
            'payable_money' => $payable_money,
            'status' => $status,
            'real_money' => 0,
            'pay_status' => $pay_status,
            'pay_type' => $pay_type,
            'coupon_id' => $coupon_id,
            'coupon_money' => $coupon_money,
            'order_type' => $data['order_type'],
            'reduce_money' => 0,
            'integral_reduce' => 0,
            'invoice_status' => 0,//发票暂时屏蔽
            'cost_price_total' => $cost_price_total
        ];
        // 插入订单信息
        $order = \app\common\model\Order::create($orderData);
        if (!$order) {
            throw new \Exception(lang('创建订单失败'));
        }
        // 计算出来的金额和提交过的来金额做对比，一致才往下走
        if (bccomp($data['payable_money'], $payable_money, 2) !== 0) {
            throw new \Exception(lang('金额校验失败') . ',' . lang('计价') . ':' . $payable_money . ':' . $data['payable_money']);
        }
        //普通快递配送订单需要计入运费，自提不计运费，wangph修改2021-4-22
        if ($send_type != 1) {
            //添加商品订单附表信息
            $order_goods_info = [
                'order_sn' => $order_no,
                'address_id' => $order_info['address']['address_id'],
                'receiver_mobile' => $order_info['address']['mobile'],
                'receiver_address' => $order_info['address']['address'],
                'receiver_name' => $order_info['address']['name'],
                'remark' => $order_info['remark'] ?? '',
                'express_price' => $order_info['express_price'] ?? 0,
                'province' => $order_info['address']['province'],
                'city' => $order_info['address']['city'],
                'district' => $order_info['address']['district'],
                'sex' => $order_info['address']['sex'],
                'label_name' => $order_info['address']['label_name']
            ];
            $res1 = Db::name('order_goods_info')->insert($order_goods_info);
            if (!$res1) {
                exception(lang('保存订单附加信息失败'));
            }
        }

        //如果pickup_id不为空，为自提类型的订单，add by wangph at 2021-4-19
        if (isset($order_info['invite_address']['pickup_id']) && $order_info['invite_address']['pickup_id']) {
            $order_pickup = [
                'order_sn' => $order_no,
                'pickup_id' => $order_info['invite_address']['pickup_id'],
                'pickup_date' => $order_info['invite_address']['pickup_date'],
                'pickup_delivery_time_id' => $order_info['invite_address']['pickup_delivery_time_id'],
                'user_pickup_id' => $order_info['invite_address']['user_pickup_id'],
                'remark' => $order_info['remark'] ?? '',
            ];
            $res_pickup = Db::name('order_pickup')->insert($order_pickup);
            if (!$res_pickup) {
                exception(lang('保存订单自提信息失败'));
            }
        }
        OrderAction::addLog($order["order_sn"], "创建订单", 0, $order["user_id"]);
        $res = [
            'order_sn' => $order_no,
            "payable_money" => $payable_money,
            'pay_status' => $pay_status,
            "timeout" => module_config('order.order_timeout') * 60,
            "is_zero" => bccomp($payable_money, 0, 2) === 0 ? 1 : 0
        ];
        return $res;
    }

    /**
     * 订单回调
     * @param Order $order
     * @param User $user
     * @author 晓风<215628355@qq.com>
     */
    public static function verify($order, $user)
    {
        $goods_thumb = self::where('order_sn', $order["order_sn"])->value("goods_thumb");
        self::where(['order_sn' => $order["order_sn"], 'order_status' => 0])->update(['order_status' => 1]);
        \app\operation\model\SystemMessage::create([
            "to_user_id" => $order['user_id'],
            "title" => '您的订单支付成功',
            "order_sn" => $order["order_sn"],
            "content" => "您的订单：" . $order["order_sn"] . ", 已支付成功" . ($order["status"] == -2 ? '，系统已自动撤销取消状态' : ''),
            "msg_type" => 2,
            "thumb" => $goods_thumb,
            "custom" => json_encode(['type' => 'order', 'id' => $order["order_sn"]])
        ]);
        OrderAction::addLog($order["order_sn"], "订单支付成功", 1, $order["user_id"]);
        return true;
    }

    /**
     * 更新订单金额
     * @author 晓风
     * @created 2021/8/5 13:45
     */
    public static function editPayableMoney(string $order_sn, float $payable_money, string $remark = "")
    {
        $order = \app\common\model\Order::where("order_sn", $order_sn)->find();
        if (!$order) {
            throw  new \Exception("未找到该订单");
        }
        if ($order["status"] != 0 || $order["pay_status"] != 0) {
            throw  new \Exception("订单状态不正确");
        }
        if ($order["edit_money"] > 0) {
            throw  new \Exception("该订单已修改过价格");
        }
        if ($payable_money <= 0) {
            throw  new \Exception("更改价格不可小于或者等于0元");
        }
        if ($payable_money > 65535) {
            throw  new \Exception("更改价格超出支付限制");
        }
        $add_money = bcsub($payable_money, $order["payable_money"], 2);

        if (mb_strlen($remark) > 100) {
            throw  new \Exception("备注内容太长");
        }
        $res = \app\common\model\Order::where("order_sn", $order_sn)->where("status", 0)->update([
            "edit_money" => ["inc", $add_money],
            "payable_money" => ["inc", $add_money],
        ]);
        if (!$res) {
            throw  new \Exception("改价失败");
        }
        $action = '平台修改订单价格';
        OrderAction::addLog($order["order_sn"], $action, $order["status"], 0, $payable_money);
        $thumb = OrderGoods::where("order_sn", $order["order_sn"])->value("goods_thumb");
        \app\operation\model\SystemMessage::create([
            "to_user_id" => $order['user_id'],
            "title" => '您的订单已修改价格',
            "order_sn" => $order['order_sn'],
            "content" => "您的商品订单："
                . $order["order_sn"] . ", 已由"
                . "平台更改价格,新的价格为：￥" . $payable_money . "，
                请确认调整价格前经过您的协商，如有问题，请及时联系平台处理。"
                . ($remark ? "\n备注信息：" . $remark : ''),
            "msg_type" => 2,
            "thumb" => $thumb ?: 0,
            "custom" => json_encode(['type' => 'order', 'id' => $order['order_sn']])
        ]);
        return true;
    }


    /* 
     * 计算结算金额
     * @param type $info
     * @return type
     */
    public static function getSettleMoney($order, $express_price)
    {


        $settle_money = bcsub($order['order_money'], $express_price, 2);
        $settle_money = bcsub($settle_money, $order['coupon_money'], 2);
        $settle_money = bcadd($settle_money, $order['edit_money'], 2);
        $settle_money = bcsub($settle_money, $order['total_refund_money'], 2);

        $result['total_settle_money'] = $settle_money;//毛利润
        $result['settle_money'] = bcsub($result['total_settle_money'], $order['cost_price_total'], 2);//净利
        return $result;
    }
}